package com.pine.common.database.interceptor;

import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
import com.baomidou.mybatisplus.extension.plugins.inner.DataChangeRecorderInnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;

import java.sql.Connection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 自定义数据变更记录拦截器
 *
 * @author pine manage
 * @since 2024-08-09
 */
@Slf4j
public class CustomDataChangeRecorderInnerInterceptor extends DataChangeRecorderInnerInterceptor {

    private static final Set<String> SKIP_MAPPED_STATEMENT_ID = new HashSet<>();

    /**
     * 表名和列名，列名用英文逗号分隔
     */
    private static final Set<String> SKIP_TABLE_NAMES = new HashSet<>();

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        MappedStatement ms = mpSh.mappedStatement();
        String mappedStatementId = ms.getId();
        boolean containsMappedId = SKIP_MAPPED_STATEMENT_ID.contains(mappedStatementId);
        if (containsMappedId) {
            log.info("数据变更拦截跳过mappedStatementId:{}", mappedStatementId);
            return;
        }

        final BoundSql boundSql = mpSh.boundSql();
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
            OperationResult operationResult;
            long startTs = System.currentTimeMillis();
            try {
                Statement statement = JsqlParserGlobal.parse(mpBs.sql());
                if (statement instanceof Insert insert) {
                    Table table = insert.getTable();
                    String name = table.getName();
                    if (SKIP_TABLE_NAMES.contains(name)) {
                        log.info("数据变更拦截跳过tableName:{}", name);
                        return;
                    }
                    operationResult = processInsert(insert, mpSh.boundSql());
                } else if (statement instanceof Update update) {
                    Table table = update.getTable();
                    String name = table.getName();
                    if (SKIP_TABLE_NAMES.contains(name)) {
                        log.info("数据变更拦截跳过tableName:{}", name);
                        return;
                    }
                    operationResult = processUpdate((Update) statement, ms, boundSql, connection);
                } else if (statement instanceof Delete delete) {
                    Table table = delete.getTable();
                    String name = table.getName();
                    if (SKIP_TABLE_NAMES.contains(name)) {
                        log.info("数据变更拦截跳过tableName:{}", name);
                        return;
                    }
                    operationResult = processDelete((Delete) statement, ms, boundSql, connection);
                } else {
                    logger.info("other operation sql={}", mpBs.sql());
                    return;
                }
            } catch (Exception e) {
                if (e instanceof DataUpdateLimitationException) {
                    throw (DataUpdateLimitationException) e;
                }
                logger.error("Unexpected error for mappedStatement={}, sql={}", ms.getId(), mpBs.sql(), e);
                return;
            }
            long costThis = System.currentTimeMillis() - startTs;
            if (operationResult != null) {
                operationResult.setCost(costThis);
                dealOperationResult(operationResult);
            }
        }
    }

    @Override
    protected void dealOperationResult(OperationResult operationResult) {
        log.info("数据变更：{}", operationResult);
    }

    @Override
    protected boolean allowProcess(String sql) {
        return super.allowProcess(sql);
    }

    @Override
    protected Map<String, Object> getUpdatedColumnDatas(String tableName, BoundSql updateSql, Statement statement) {
        return super.getUpdatedColumnDatas(tableName, updateSql, statement);
    }

    public static void appendSkipMappedStatementId(String mappedStatementId) {
        SKIP_MAPPED_STATEMENT_ID.add(mappedStatementId);
    }

    public static void appendSkipMappedStatementIdSet(Set<String> mappedStatementIdSet) {
        SKIP_MAPPED_STATEMENT_ID.addAll(mappedStatementIdSet);
    }

    public static void appendSkipTableName(String tableName) {
        SKIP_TABLE_NAMES.add(tableName);
    }

    public static void appendSkipTableNameSet(Set<String> tableNameSet) {
        SKIP_TABLE_NAMES.addAll(tableNameSet);
    }

}
